package com.glacier.utils;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * date 2022-01-13 12:17
 *
 * @author glacier
 * @version 1.0
 */
public class MethodHandlesUtils {
	private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
			| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC;
	private static Constructor<MethodHandles.Lookup> java8LookupConstructor;
	private static Method privateLookupInMethod;
	
	static {
		//先查询jdk9 开始提供的java.lang.invoke.MethodHandles.privateLookupIn方法,
		//如果没有说明是jdk8的版本.(不考虑jdk8以下版本)
		try {
			privateLookupInMethod = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
		} catch (NoSuchMethodException e) {
			privateLookupInMethod = null;
		}
		//jdk8
		//这种方式其实也适用于jdk9及以上的版本,但是上面优先,可以避免 jdk9 反射警告
		if (privateLookupInMethod == null) {
			// JDK 1.8
			try {
				java8LookupConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
				java8LookupConstructor.setAccessible(true);
			} catch (NoSuchMethodException e) {
				//可能是jdk8 以下版本
				throw new IllegalStateException(
						"There is neither 'privateLookupIn(Class, Lookup)' nor 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.",
						e);
			} catch (Exception e) {
				java8LookupConstructor = null;
			}
		}
	}
	
	public static MethodHandle findMethodHandle(Method method) {
		System.out.println("当前jdk版本\t" + System.getProperty("java.version"));
		MethodHandle methodHandle = null;
		try {
			// jdk8
			if (privateLookupInMethod == null) {
				System.out.println("jdk8");
				methodHandle = java8LookupConstructor.newInstance(method.getDeclaringClass(), ALLOWED_MODES)
						.unreflectSpecial(method, method.getDeclaringClass());
			} else {
				System.out.println("jdk9及以上");
				methodHandle = ((MethodHandles.Lookup) privateLookupInMethod.invoke(
						null, method.getDeclaringClass(), MethodHandles.lookup()))
						.findSpecial(method.getDeclaringClass(),
								method.getName(),
								MethodType.methodType(
										method.getReturnType(),
										method.getParameterTypes()),
								method.getDeclaringClass());
			}
		} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
		}
		return methodHandle;
	}
}
